class DigitalPuzzleSolution {
  constructor(needNum = 5, uid = 0) {
    this.targetNeedNum = needNum
    if (!uid) {
      this.getUserInfo()
    } else {
      this.uid = uid
    }
    this.cache = null
    if (!this.isNative(console.log)) {
      // var _frame = document.createElement('iframe');
      // document.body.appendChild(_frame);
      // this.console = _frame.contentWindow.console
      this.console = console
    } else {
      this.console = console
    }
    this.LasVegasCalcNum = 1000000
    this.codeMap = null
    window.calcSum = this.calcSum
    window.calcSum2 = this.calcSum2
    window.getDigitalPuzzle = this.getDigitalPuzzle
    this.typeMax = 2
  }

  isNative(method) {
    return !!method && (/{\s*\[native code\]\s*}/.test(method + ''))
  }

  setNeedNum = (needNum = 5) => {
    this.targetNeedNum = needNum
  }
  setLasVegasCalcNum = (LasVegasCalcNum = 1000000) => {
    this.LasVegasCalcNum = LasVegasCalcNum
  }
  post = (url, done) => {
    var httpRequest = new XMLHttpRequest()
    httpRequest.open('POST', url, true)
    httpRequest.setRequestHeader('Content-type', 'application/json; charset=utf-8')
    httpRequest.setRequestHeader('authorization', 'Bearer ' + this.authorization)
    httpRequest.send()
    let that = this
    httpRequest.onreadystatechange = function () {
      if (httpRequest.readyState == 4 && httpRequest.status == 200) {
        var json = httpRequest.responseText
        done(JSON.parse(json))

      }
    }
  }
  get = (url, done) => {
    var httpRequest = new XMLHttpRequest()
    httpRequest.open('GET', url, true)
    httpRequest.send()
    let that = this
    httpRequest.onreadystatechange = function () {
      if (httpRequest.readyState == 4 && httpRequest.status == 200) {
        console.log(httpRequest)
        var json = httpRequest.responseText
        done && done(JSON.parse(json))
      }
    }
  }
  getUserInfo = () => {
    let that = this
    this.get('https://api.juejin.cn/user_api/v1/user/get', function (res) {
      if (res.err_no === 0) {
        that.uid = res.data.user_id
      } else {
        console.log('id获取失败')
      }
    })
  }
  getAuth = (done) => {
    let that = this
    this.get('/get/token', function (res) {
      that.authorization = res.data
      done()
    })
  }
  getDigitalPuzzle = (type = 0) => {
    if (type >= 0 && type < this.typeMax) {
      this.type = type
    }
    if (!this.uid) {
      this.console.log('请先设置用户id 如:solution.setUid(12345678)')
      return
    }
    let that = this
    this.getAuth(function () {
      that.post('https://juejin-game.bytedance.com/game/num-puzz/ugc/start?uid=' + that.uid + '&time=' + Date.now(), that.run)
    })

  }
  run = (res) => {
    if (res.code === 0) {
      let arg = this.getArgument(res.data)
      if (arg) {
        this.console.log('数字：' + arg.nums + '\n符号：' + arg.options + '\n目标：' + arg.target + '\n所需' + this.targetNeedNum + '个解（如需要更多解请使用setNeedNum）\n解：')
        if (this.type == 0) {
          this.calcSum(arg.nums, arg.options, arg.target)
        } else if (this.type == 1) {
          this.calcSum2(arg.nums, arg.options, arg.target)
        }
        this.console.log('结束!')
        return
      }
    }
    this.console.log('获取参数错误')
  }

  getArgument(data) {
    let target = data.target
    let nums = []
    let options = []
    let optionMap = ['+', '-', '*', '/']
    for (let i of data.map) {
      for (let j of i) {
        if (j >= 0) {
          if (j % 1 === 0) {
            nums.push(j)
          } else {
            let n = j * 10 - 3
            if (n >= 0 && n < 4) {
              options.push(optionMap[n])
            }
          }
        }
      }
    }
    return { target, nums, options }
  }

  setUid = (uid) => {
    this.uid = uid
  }

  calc(a, option, b) {
    let res
    let formula
    switch (option) {
      case '+':
        res = a.value + b.value
        break
      case '-':
        res = a.value - b.value
        break
      case '*':
        res = a.value * b.value
        break
      case '/':
        res = a.value / b.value
        break
      case '.':
        res = Number(a.value + '' + b.value)
        break
      default :

    }
    formula = a.formula + option + b.formula
    if (res < 0) {
      res = NaN
    } else if (res === Infinity) {
      res = NaN
    } else if (res % 1 !== 0) {
      res = NaN
    }
    return {
      value: res,
      formula: '(' + formula + ')',
    }
  }

  calcSum = (nums, options, target) => {
    let cha = nums.length - 1 - options.length
    this.cache = {}
    if (cha) {
      options.push(...'.'.repeat(cha))
    }
    this.nowNum = 0
    options.sort()
    let len = nums.length
    for (let i = 0; i < len; i++) {
      let num = nums[i]
      this.cache[num] = {
        value: num,
        formula: '' + num,
      }
      nums[i] = '' + num
    }
    nums.sort()
    this.calcLoop(nums, options, target)
    this.cache = null
  }
  calcLoop = (nums, options, target) => {
    let cache = this.cache
    if (nums.length === 1 && options.length == 0 && cache[nums[0]].value == target) {
      this.console.log(nums[0])
      this.nowNum++
      if (this.nowNum >= this.targetNeedNum) {
        return true
      }
      return// nums[0]
    }
    nums = [...nums]
    nums.sort()
    let key = [...nums].join() + '|' + options.join()
    if (cache[key]) {
      return
    }
    cache[key] = true
    for (let i of '*+-/.') {
      let index = options.indexOf(i)
      if (index >= 0) {
        let newOptions = [...options]
        newOptions.splice(index, 1)
        let len = nums.length
        for (let j = 0; j < len - 1; j++) {
          let newNums = [...nums]
          newNums.splice(j, 1)
          for (let k = j + 1; k < len; k++) {
            let newNums2 = [...newNums]
            //newNums2.splice(newNums2.indexOf(nums[k]), 1);
            let newNum = this.calc(cache[nums[j]], i, cache[nums[k]])
            if (isNaN(newNum.value)) {
            } else {
              cache[newNum.formula] = newNum
              newNums2[k - 1] = newNum.formula//[newNum.formula, ...newNums2]
              if (this.calcLoop(newNums2, newOptions, target)) return true
            }
            if (i != '+' && i != '*') {
              let newNum = this.calc(cache[nums[k]], i, cache[nums[j]])
              if (isNaN(newNum.value)) {
              } else {
                cache[newNum.formula] = newNum
                newNums2[k - 1] = newNum.formula
                if (this.calcLoop(newNums2, newOptions, target)) return true
              }
            }
          }
        }
      }
    }
  }
  // calcSum=(nums, options, target)=>{
  //   let cha = nums.length - 1 - options.length;
  //   this.cache = {};
  //   if (cha) {
  //     options.push(...'.'.repeat(cha))
  //   }
  //   this.nowNum = 0
  //   options.sort()
  //   let len = nums.length
  //   for (let i = 0; i < len; i++) {
  //     let num = nums[i]
  //     this.cache[num] = {
  //       value: num,
  //       formula: '' + num,
  //     }
  //     nums[i] = '' + num;
  //   }
  //   nums.sort()
  //   this.calcLoop(nums, options, target)
  //   this.cache = null;
  // };
  calcSum2 = (nums, options, target) => {
    let cha = nums.length - 1 - options.length
    this.cache = {}
    if (cha) {
      options.push(...'.'.repeat(cha))
    }
    this.nowNum = 0
    options.sort()
    let len = nums.length
    nums.sort()
    for (let i = 0; i < len; i++) {
      let num = nums[i]
      nums[i] = {
        value: num,
        formula: '' + num,
      }
    }
    this.calcLoop2(nums, options, target)
    this.cache = null
  }
  calcLoop2 = (nums, options, target) => {
    let cache = this.cache
    if (nums.length === 1 && options.length == 0 && nums[0].value == target) {
      this.console.log(nums[0].formula)
      this.nowNum++
      if (this.nowNum >= this.targetNeedNum) {
        return true
      }
      return// nums[0]
    }

    let nums2 = nums.map((v) => v.value)
    nums2.sort()
    let key = nums2.join() + '|' + options.join()
    if (cache[key]) {
      return
    }
    cache[key] = true
    for (let i of '*+-/.') {
      let index = options.indexOf(i)
      if (index >= 0) {
        let newOptions = [...options]
        newOptions.splice(index, 1)
        let len = nums.length
        for (let j = 0; j < len - 1; j++) {
          let newNums = [...nums]
          newNums.splice(j, 1)
          for (let k = j + 1; k < len; k++) {
            let newNums2 = [...newNums]
            //newNums2.splice(newNums2.indexOf(nums[k]), 1);
            let newNum = this.calc(nums[j], i, nums[k])
            if (isNaN(newNum.value)) {
            } else {
              newNums2[k - 1] = newNum//[newNum.formula, ...newNums2]
              if (this.calcLoop2(newNums2, newOptions, target)) return true
            }
            if (i != '+' && i != '*') {
              let newNum = this.calc(nums[k], i, nums[j])
              if (isNaN(newNum.value)) {
              } else {
                newNums2[k - 1] = newNum
                if (this.calcLoop2(newNums2, newOptions, target)) return true
              }
            }
          }
        }
      }
    }
  }
}

export default DigitalPuzzleSolution


// let solution=new DigitalPuzzleSolution(3, 184373682388791)
// let solution=new DigitalPuzzleSolution()

// solution.calcSum2([2,8,8,8,8,8,8,8],['-','-','/','/','+','+'],13)
// calcSum2([2,2,3,4,5,6,15,26,62],['-','/','/','*','*','+','+','-'],2610)
// solution.getDigitalPuzzle(1)
// getDigitalPuzzle(1)
